#!/bin/bash
#THIS DRIVER WRITTEN BY MATTHEW VONA ARB FOR THE TAPI Ultra Fine Particulate Model 651
#FOR QUESTIONS WRITE mailto:mvona@arb.ca.gov
#UPDATE: 5/31/2013;   
#-------------------------------------------------------------
#!/usr/bin/env bash
# THIS IS THE FUNCTION CALLED IN THE EVENT THAT CTRL+C IS PRESSED
#DIR returns operating directory of the testd script
DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

cleanup()
{
	echo "Caught Signal ... cleaning up."
		if !(rm -f Data/.locked > /dev/null); then
			exit 1
		fi
	echo "Done cleanup ... quitting."
	exit 1
}
#############################################################################################
# BEGIN MAIN BAM ERROR EXPANSION FUNCTION:MJV 6/17/2011
# THIS CODE HAS NOT BEEN IMPLEMENTED YET. IT COULD EASILY BE DONE BY CALLING THE INSTRUMENT
# FOR ALL ERRORS, BUT AT THE TIME OF THIS WRITING IT WAS NOT REQUESTED.--MJV
############################################################################################
function expand {
	#INITIALIZE THE INPUT VALUE
	input=$1
	#INITIALIZE THE RESULT
	result=''
	#INITIALIZE THE REMAINDER
	remainder='0'
	#INITIALIZE PARSED CODES
	parsedcodes=''
	#initialize the errors
	errors=''
	for i in 9000 8000 4000 2000 1000 800 400 200 100 80 40 20 8 4 2 1
	do
		remainder=`expr $input "-" $i`
		#echo "$input - $i = $remainder"
		if [[ $remainder -gt 0 ]]; then
			result="$result $i"
			input=$remainder  
		#echo "input is now $input"
		elif [[ $remainder -lt 0 ]]; then
			false
		#echo $result
		elif [[ $remainder -eq 0 ]]; then
			result="$result $i"
			echo "$result"
			exit 0
		fi
	done
}
##############################################################################################
# BEGIN MAIN TAPI 651 ERROR TRANSLATION FUNCTION:MJV 6/17/2011
# THIS CODE HAS NOT BEEN IMPLEMENTED YET. IT COULD EASILY BE DONE BY CALLING THE INSTRUMENT
# FOR ALL ERRORS, BUT AT THE TIME OF THIS WRITING IT WAS NOT REQUESTED.--MJV
##############################################################################################
function translate {
	#initialize the variables
	error_string=''
	errors=''
	
	for c in $@
	do
		case $c in
		
		9000)
		errors="DISCONNECTED, $errors"
			;;
		8000)
			errors="RESERVED, $errors"
			;;
		4000)
			errors="SERVICE REMINDER, $errors"
			;;
		2000)
			errors="RESERVED, $errors"
			;;
		1000)
			errors="WARMUP, $errors"
			;;
		800)
			errors="WATER SEPERATOR TEMPERATURE, $errors"
			;;
		400)
			errors="NOZZLE PRESSURE, $errors"
			;;
		200)
			errors="ABSOLUTE PREASSURE, $errors"
			;;
		100)
			errors="PULSE HEIGHT FAULT, $errors"
			;;
		
		80)
			errors="CONCENTRATION OVER RANGE, $errors"
			;;
		40)
			errors="WATER LEVEL, $errors"
			;;
		20)
			errors="LASER STATUS, $errors"
			;;
		8)
			errors="VACUUM LEVEL, $errors"
			;;
		4)
			errors="OPTICS TEMPERATURE, $errors"
			;;
		2)
			errors="GROWTH TUBE TEMPERATURE, $errors"
			;;
		1)
			errors="CONDITIONER TEMPERATURE, $errors"
			;;
		
		esac
	
	done	
	echo $errors | sed 's/,$/\r/g'
}
############################################################################################
#THIS CODE HAS NOT BEEN IMPLEMENTED YET. IT COULD EASILY BE DONE BY CALLING THE INSTRUMENT
#FOR ALL ERRORS, BUT AT THE TIME OF THIS WRITING IT WAS NOT REQUESTED.--MJV
#BEGIN INTERPRET_ERROR FUNCTION
#THIS FUNCTION TAKES A NUMERICAL BAM ERROR ARGUEMENT
#AND RETURNS (VIA ECHO IN DEBUG) THE ENGLISH ERRORS
#BY SETTING A VARIABLE $error_string
############################################################################################ 
function interpret_error {
	this_expansion=`expand $@` 
	error_string=`translate "$this_expansion"`
	#DEBUG
	echo $error_string
}

rprt_nm= ; dvc_prt= ; bd_rt= ; cmnd1="RIS\r" ; wt_rsp1= ; bd_rt= ; rsp_lngth= ; izs_val= ;
dvc_prt= ; frqncy= ; dtr= ; ctr= ; p8n1= ; izs= ; saroad= ; p8n1= ; id_nmbr= ; factor="1000" ; instance=1;
occurrence=3;
##INITIALIZE THE BUFFER
return='NOT WRITTEN TO'
##loop through all variables and parameters
##Note that the position of the colon indicates the presence
##of a parameter to collect. The h parameter simply
##returns the help message.

while getopts “hr:s:d:b:c:w:l:t:C:p:I:” OPTION
do
case $OPTION in
	I)
	instance=$OPTARG
	;;
	s)
	saroad=$OPTARG
	;;
	r)
	rprt_nm=$OPTARG
	;;
	d)
	dvc_prt=$OPTARG
	;;
	b)
	bd_rt=$OPTARG
	;;
	w)
	wt_rsp1=$OPTARG
	;;
	l)
	rsp_lngth=$OPTARG
	;;
	t)
	dtr_val=$OPTARG
	;;
	C)
	ctr_val=$OPTARG
	;;
	p)
	p8n1_val=$OPTARG
	;;
	M)
	factor=$OPTARG
	;;
	h)
	help_me
	exit 1
	;;
	*)
usage
exit 1
;;
esac
done

#BEGIN WRITE
#THIS FUNCTION HANDELS READ RIGHT HANDSHAKES BETWEEN VARIOUS DRIVERS
#IN GENERAL, THE DATA IS PLACED IN THE VARIABLE $TIMESTAMP PRIOR TO CALL
#NEXT, IT OPENS THE DATA DIRECTORY AND LOOKS FOR THE .locked FILE
#IF IT EXISTS, IT HANGS OUT UNTIL IT DOESN'T EXIST. AT THAT POINT
#IT CREATES A .locked FILE, AND APPENDS TO THE DATA RECORD.
#FINALLY IT REMOVES THE .locked FILE IT HAS CREATED.
#THIS FUNCTION ALSO PARSES THE .DISABLED FILE IN THE DRIVER FOLDER
#AND TAKES A CHANNEL OFF LINE BY PREPENDING --DISABLED-- TO EACH LINE OF CONTENT IN THE DATA DIRECTORY
#written by MJV
write()
{
#CONFIRM THAT THERE IS NO HIDDEN FILE CALLED '.locked'
	while [ -e "Data/.locked" ];
	do
		sleep .2;
	done	
	echo "LOCKED" > "Data/.locked";
	#PARSE THE .disabled File
	channel_status=`cat "${DIR}/.disabled${instance}"`
	#IF THE FILE DOSEN'T EXIT OR IS NOT ACCESIBLE
	#MAKES SURE A VALUE OF 0 IS USED
	if [ `expr $channel_status + 0` -eq 1 ]; then
		if !(echo "$data" |sed 's/^/'$timestamp'/g'| sed 's/$/'\-\-DISABLED\-\-'/g'| tee -a "Data/$datafile"); then
			echo "$PWD $0 GENERAL FAILURE WRITING TO RECORD: $#";
			cleanup;
		fi
	else
		if !(echo "$data" | sed 's/^/'$timestamp'/g' | tee -a "Data/$datafile" && echo $this_error); then
			echo "$PWD $0 GENERAL FAILURE WRITING TO RECORD: $#";
			cleanup;
		fi
	fi
	if !(rm -f "Data/.locked" > /dev/null); then
		echo "GENERAL FAILURE REMOVING LOCK: $#";
		exit 1;
	fi
	if !(timestamp="" && data=""); then
		echo "GENERAL FAILURE CLEARING WRITE BUFFER: $#";
		cleanup;
	fi	
}

#THIS IS THE ACTUAL COMMUNICATION WITH THE INSTRUMENT
#THE TIMER CONTROL. AT THE BEGINING IT TESTS FOR THE 
#EXISTENCE IF TOGGLE VARRIABLES WHICH ARE UTILIZED BOTH BY
#THE COMM ROUTINE runit() AND THE DIAGNOSTICS DATA ROUTINE
#diag()
runit()
{
	#IF THE -d, -C, or -p variable is set to on, then append those parameters
	#to the sjinn communication string. If the -i parameter is passed, diag will
	#test for IZS specific routines.
	if [ "$dtr_val" = "on" ]; then
	 	dtr="--dtr" 
	fi
	if [ "$ctr_val" = "on" ]; then
 		ctr="--ctr"
	fi
	if [ "$p8n1_val" = "on" ]; then
	 	p8n1="-p8n1"
	fi
	#Date stamp
	today=`date "+%Y-%d-%m"`
	timestamp=$rprt_nm::`date "+%m-%d-%Y_%H:%M"`:::
	#THIS IS THE NAME OF THE DATA FILE RIGHT NOW
	datafile="$saroad-$today"
	
	##################################################################################################
	#
	#BEGIN NEW TAPI 651 COM && DIAGNOSTICS ROUTINE:
	##################################################################################################
	this_name=$(cat ${DIR}/.usr$instance | grep -o "\-r [A-Za-z0-9_]*" | awk '{ print $2 }')
	thishour=`date "+%H"`
	thisalert="errors${thishour}"
	data=`timeout 10 sjinn -b${bd_rt} -d ${dvc_prt} -s "RIS\r" -r$rsp_lngth -w$wt_rsp1 $p8n1 | grep -v ^$ | tr '\n' ',' | awk -v occ=${occurrence} -F ',' '{ print "PRT_CNC_"occ"="$1+0" LV_TM_"occ"="$2 " INLT_PRS_"occ"="$4" NZZL_PRS_"occ"="$5" INLT_FLW_MD_"occ"="$6" PLS_HGHT_"occ"="$8" OPTCS_TMP_"occ"="$9" GRWTH_TB_TMP_"occ"="$10" CNDTNR_TMP_"occ"="$11" H2O_SEP_TMP_"occ"="$12" H2O_RES_"occ"="$13 }'`
	#CHECK NOZZLE PRESSURE
	echo "$data" | grep -o NZZL_PRS_[0-9]=[0-9]* | sed 's/=/ /g' | awk -v name=$this_name -v stamp="$timestamp" '{ if ( ($2+0) < 90) { print stamp " The "name" Nozzle Pressure seems low: " $0} }' >> "Data/$thisalert"
	#CHECK PULSE HEIGHT
	echo "$data" | grep -o PLS_HGHT_[0-9]=[0-9]* | sed 's/=/ /g' | awk -v name=$this_name -v stamp="$timestamp" '{ if ( ($2+0) < 700 ) { print stamp " The "name" Pulse Height under 700mv. Current Value: " $0} }' >> "Data/$thisalert"
	#CHECK WATER RESERVOIR
	echo "$data" | grep -o H2O_RES_[0-9]=[0-9]* | sed 's/=/ /g' | awk -v name=$this_name -v stamp="$timestamp" '{ if ( ($2+0) > 0 ) { print stamp " The "name" Water Reservoir Empty. Current Value: " $0} }' >> "Data/$thisalert"
	#CHECK VACUUM PRESSURE
	vac_pres=`timeout 10 sjinn -b${bd_rt} -d ${dvc_prt} -s "RPV\r" -r5 -w$wt_rsp1 $p8n1 | sed 's/[.]*//'`
	#Make a Variable for Inlet Pressure
	INLT_PRS=`echo $data | grep -o INLT_PRS_[0-9]=[0-9]* | sed 's/=/ /g' | awk '{ print $2 }' `
	#ADD VACUUM PRESSURE TO THE DATA STREAM
	data="$data VAC_PRS_${occurrence}=${vac_pres}"
	#IF VAC PRESSURE > .5 INLET PRESSURE, THROW AN ERROR
	echo "$data" | grep -o VAC_PRS_[0-9]=[0-9]* | sed 's/=/ /g' | awk -v INLT_PRS=${INLT_PRS} -v name=$this_name -v stamp="$timestamp" '{ if ( ($2+0) > .5*INLT_PRS ) { print stamp " The "name" Vacuum Pres > .5 TIME INLET PRESSURE. Current Value: " $0} }' >> "Data/$thisalert"
	#GET ERROR CODE FROM INSTRUMENT	
	errors=`timeout 10 sjinn -b${bd_rt} -d ${dvc_prt} -s "RIE\r" -r4 -w$wt_rsp1 $p8n1 | sed 's/[.]*//'`	
	if [ "$errors" == "" ]; then
		errors=9000
	fi
	#INTERPRET ERRORS
	translate_errors=`interpret_error $errors`
	#add errors to data stream
	data="$data ERR_CODE_${occurrence}=${errors} TRANSLATE_ERR_${occurrence}=${translate_errors}"
	#CHECK ERROR CODES
	echo "$data" | grep -o ERR_CODE_[0-9]=[0-9]* | sed 's/=/ /g' | awk -v translate="$translate_errors" -v name=$this_name -v stamp="$timestamp" '{ if ( ($2+0) > 0 ) { print stamp " The "name" has the following error flags: "translate} }' >> "Data/$thisalert"	
	#IF THE INSTRUMENT IS DISCONNECTED SET ALL VALUES TO NULL
	if [[ "${vac_pres}" == '.....' ]] || [[ "${vac_pres}" == '..'* ]] || [[ "${INLT_PRS}" == '' ]];then
		data="PRT_CNC_"${occurrence}"=-999 LV_TM_"${occurrence}"=-999 INLT_PRS_"${occurrence}"=-999 NZZL_PRS_"${occurrence}"=-999 INLT_FLW_MD_"${occurrence}"=-999 PLS_HGHT_"${occurrence}"=-999 OPTCS_TMP_"${occurrence}"=-999 GRWTH_TB_TMP_"${occurrence}"=-999 CNDTNR_TMP_"${occurrence}"=-999 H2O_SEP_TMP_"${occurrence}"=-999 H2O_RES_"${occurrence}"=-999 VAC_PRS_"${occurrence}"=-999 ERR_CODE_"${occurrence}"=9000"
	fi
	###################################################################################################
	#
	#BEGIN THE TIME CHECKING AND SETTING ROUTINE
	#
	###################################################################################################
	if [ `date '+%H:%M' ` == "00:05" ] || [ `date '+%H:%M' ` == "06:05" ] || [ `date '+%H:%M' ` == "12:05" ] || [ `date '+%H:%M' ` == "18:05" ]; then
		timeset=`date '+%Y,%m,%d,%H,%M,%S'`	
		timeout_return=`timeout 10 sjinn -b${bd_rt} -d ${dvc_prt} -s "SR,${timeset}\r" -r2 -w$wt_rsp1 $p8n1`
		if [ "$timeout_return" != "OK" ]; then
			echo "$this_name $timestamp Problem setting time!" >> "Data/$thisalert"
		fi
	fi  
	#######REPORT ERRORS
	this_hour=`date '+%H'`
	write
}
#THIS IS THE FUNCTION RUN IN THE EVENT THAT THE REQUIRED PARAMETERS
#ARE NOT ENTERED.
usage()
{
clear| grep -v '<esc>' 
cat << EOF
usage: $0 options
OPTIONS:
-h	display help
-r	rprt_nm [rn]
-d	dvc_prt [eg, /dev/ttyS0, /dev/ttyUSB1] 
-b	bd_rt [2400,4800,9600,etc.]
-w	wt_rsp1 [seconds]
-l	rsp_lngth [characters]
-s	SAROAD number of the station
-t	[on/off] (OPTIONAL)
-C	[on/off] (OPTIONAL)
-p	[on/off] (OPTIONAL)
-I	instance
Last updated 06/04/2013

This driver was made using a hard coded command string of RIS\r.
This was done because the program is performing a one for one label 
and substitution on the string. The following diagnostics are being monitored:
	Pulse height < 700 and > 300 mv
	Nozzle pressure 90-100%
	Vacuum pressure < 1/2* Inlet Pressure
Finally, it sets the time 4 times per day at 00:05, 06:05, 12:05, and at 18:05.
The time is in GMT - 8, synchronized to NIST time servers. 
  

EOF
}

#HELP FUNCTION GIVES BACKGROUND ON THIS DRIVER
help_me ()
{
	clear
	cat .help
	exit 0
}
def_opts ()
{
	clear
	cat .options
}

##CLEAR ALL VARIABLES AND SET DEFAULTS               

#CHECK FOR THE CRITICAL PARAMETERS, IF THEY ARE NOT PRESENT THEN
#RUN THE USAGE FUNCTION WHICH RETURNS THE INSTRUCTIONS FOR PROPER USAGE
#MUST HAVE r n d b c w l i s
if [ -z "$rprt_nm" ] || [ -z "$dvc_prt" ] || [ -z "$bd_rt" ] || [ -z "$cmnd1" ] ||
[ -z "$wt_rsp1" ] || [ -z "$rsp_lngth" ] || [ -z "$saroad" ] 
then
	usage
	exit
fi

#INITIALIZED THE RAN VARIABLE. THIS IS USED TO TOGGLE THE TIMER GATE
ran=0

#TRAP CTRL+C
trap cleanup 1 2 3 6 15
#SETUP THE OPERATING LOOP
while :
do
	second=`date "+%S"`
	if [ "$second" -eq "00" ] && [ "$ran" -eq "0" ]; then
		#RUN THE RUNIT FUNCTION
		runit
		#TOGGLE THE RAN VARIABLE SO THAT THE PROCESS WILL NOT REPEAT
		#UNTIL THE BEGINNING OF THE NEXT MINUTE.
		ran=1
		#WAIT FOR COMPLETION OF THE RUNIT FUNCTION AND THE VARIABLE
		#SET
		sleep 1
		#AND WAIT ONE SECOND BEFORE CHECKING AGAIN
	else 
		#WAIT ONE SECOND
		sleep .5
		#SET RAN TO 0
		ran=0
	fi
done
exit
